home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / BackSpaceViews / MazeView.BackModule / MazeView.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  20.9 KB  |  777 lines

  1. // MazeView by David Bau
  2. // Copyright 1994 by David Bau.  All rights reserved.
  3. //
  4. // I feel silly even saying this, but please don't charge for this
  5. // module or any product containing a portion or modification of it,
  6. // and when distributing, please give credit where credit is due.
  7. //
  8. // If you add anything to this module or derive anything interesting
  9. // from it, please let me know!
  10. //
  11. // I'll probably be bau@cs.cornell.edu for a while.
  12. //
  13. // David Bau 01/21/94
  14. // 777 South Avenue, Weston, MA 02193
  15. //
  16. // 01/13/94 Fixed panel maze so that it doesn't change the first
  17. // time a main-view maze begins drawing; it only changes after the
  18. // main-view maze finishes.  Also, fixed drawSelf to be more efficient.
  19. // Removed a floaing-point problem with the exponential speed dial.
  20. // Changed the default colors.
  21. //
  22. // 01/14/94 Eliminated lockFocus when [self canDraw]==NO
  23. // 01/14/94 Modified by Richard Hess (rhess@consilium.com)
  24. // Fixed startup problems when using MazeView with Engage! Desktop...
  25. //
  26. // 02/01/94 Changed maze generation algorithm to make trickier mazes,
  27. // using a second RandomIntPicker.
  28.  
  29. #import <appkit/appkit.h>
  30. #import <defaults/defaults.h>
  31. #import <libc.h>
  32. #import <time.h>
  33. #import <dpsclient/wraps.h>
  34. #import "MazeView.h"
  35. #import "RandomIntPicker.h"
  36. #import "Thinker.h"
  37.  
  38.  
  39. static void ColorToString(NXColor color, char *str);
  40. static NXColor ColorFromString(const char *str);
  41. static NXColor RandomColor();
  42.  
  43. /* the kind of Maze view that appears in the control panel */
  44. @implementation StaticMazeView
  45.  
  46. - initMaze
  47. {
  48.     /* frame the field 2 cells bigger than the view */
  49.     ncols=MIN((((int)bounds.size.width-wallSize)/cellSize+2),MAXCOLS);
  50.     nrows=MIN((((int)bounds.size.height-wallSize)/cellSize+2),MAXROWS);
  51.     if (ncols<4) ncols=4;
  52.     if (nrows<4) nrows=4;
  53.     xoffset=((int)bounds.size.width-(ncols-2)*cellSize-wallSize)/2-cellSize;
  54.     yoffset=((int)bounds.size.height-(nrows-2)*cellSize-wallSize)/2-cellSize;
  55.  
  56.     /* allocate grid */
  57.     if (Grid) NX_ZONEREALLOC([self zone],Grid,mazecell,nrows*ncols);
  58.     else NX_ZONEMALLOC([self zone],Grid,mazecell,nrows*ncols);
  59.  
  60.     [self computeMaze];
  61.  
  62.     /* choose a random starting place and direction */
  63.     icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
  64.     dcur=random()%4;
  65.     Grid[icur].bdir=4;
  66.     ifirst=icur;
  67.     Grid[icur].next=(-1);
  68.  
  69.     /* set the current colors */
  70.     curWallColor=wallColor;
  71.     curPathColor=pathColor;
  72.  
  73.     /* clear drawing buffers */
  74.     wallRectListSize=0;
  75.     pathRectListSize=0;
  76.  
  77.     /* quickly do half-of-a-maze-exploration */
  78.     countDown=((nrows-2)*(ncols-2)*2-2)/2;
  79.     while (countDown--) {
  80.         int inew;
  81.         inew=icur;
  82.         switch (dcur) {
  83.         case 0:
  84.             if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
  85.         case 1:
  86.             if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
  87.         case 2:
  88.             if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
  89.         case 3:
  90.             if (!Grid[icur].southwall) {dcur=3; inew=icur-ncols; break;}
  91.             if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
  92.             if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
  93.             if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
  94.         }
  95.         if (Grid[icur].bdir==dcur) {
  96.             Grid[icur].bdir=(-1);
  97.         } else {
  98.             Grid[inew].next=ifirst;
  99.             ifirst=inew;
  100.             Grid[inew].bdir=(dcur+2)%4;
  101.         }
  102.         icur=inew;
  103.         dcur=(dcur+3)%4;
  104.     }
  105.  
  106.     return self;
  107. }
  108.  
  109. - drawSelf:(const NXRect *)rects :(int)rectCount
  110. {
  111.     PSsetgray(NX_BLACK);
  112.     if (rectCount>1) {
  113.         NXRectFillList(rects+1,rectCount-1);
  114.     } else {
  115.         NXRectFill(rects);
  116.     }
  117.     return [super drawSelf:rects:rectCount];
  118. }
  119.  
  120. /* main view can tell panel view what colors to use */
  121. - setWallColor:(NXColor)wc pathColor:(NXColor)pc
  122. {
  123.     curWallColor=wallColor=wc;
  124.     curPathColor=pathColor=pc;
  125.     return self;
  126. }
  127.  
  128. @end
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135. @implementation MazeView
  136.  
  137. - initMaze
  138. {
  139.     int t;
  140.     /* frame the field 2 cells bigger than the view */
  141.     if (!cellSize) NXLogError("MazeView: cellSize is 0 in initMaze\n");
  142.     ncols=MIN((((int)bounds.size.width-wallSize)/cellSize+2),MAXCOLS);
  143.     nrows=MIN((((int)bounds.size.height-wallSize)/cellSize+2),MAXROWS);
  144.     if (ncols<4) ncols=4;
  145.     if (nrows<4) nrows=4;
  146.     t=(int)bounds.size.width-(ncols-2)*cellSize-wallSize;
  147.     xoffset=(t<0 ? t/2-cellSize : random()%(t+1)-cellSize);
  148.     t=(int)bounds.size.height-(nrows-2)*cellSize-wallSize;
  149.     yoffset=(t<0 ? t/2-cellSize : random()%(t+1)-cellSize);
  150.  
  151.     /* allocate grid */
  152.     if (Grid) NX_ZONEREALLOC([self zone],Grid,mazecell,nrows*ncols);
  153.     else NX_ZONEMALLOC([self zone],Grid,mazecell,nrows*ncols);
  154.     [self computeMaze];
  155.     countDown=(-1);
  156.     ifirst=(-1);
  157.     return self;
  158. }
  159.  
  160. - computeMaze
  161. {
  162.     RandomIntPicker *picker1, *picker2;
  163.  
  164.     /* clear the "last" fields */
  165.     [self clearMaze];
  166.  
  167.     /* compute the maze */
  168.     icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
  169.     Grid[icur].bdir=4;
  170.     Grid[icur].eastwall=Grid[icur].northwall=
  171.         Grid[icur].westwall=Grid[icur].southwall=1;
  172.     picker1=[[RandomIntPicker alloc] init];
  173.     picker2=[[RandomIntPicker alloc] init];
  174.     while (1) {
  175.         [picker1 reset];
  176.         [picker2 reset];
  177.         if (Grid[icur+1].bdir<0) {
  178.             [picker1 insert:0];
  179.             if (Grid[icur+2].bdir<0) [picker2 insert:0];
  180.         }
  181.         if (Grid[icur+ncols].bdir<0) {
  182.             [picker1 insert:1];
  183.             if (Grid[icur+2*ncols].bdir<0) [picker2 insert:1];
  184.         }
  185.         if (Grid[icur-1].bdir<0) {
  186.             [picker1 insert:2];
  187.             if (Grid[icur-2].bdir<0) [picker2 insert:2];
  188.         }
  189.         if (Grid[icur-ncols].bdir<0) {
  190.             [picker1 insert:3];
  191.             if (Grid[icur-2*ncols].bdir<0) [picker2 insert:3];
  192.         }
  193.         if ([picker1 count]) {
  194.             int choice;
  195.             choice=[picker2 count] ? [picker2 choice] : [picker1 choice];
  196.             switch(choice) {
  197.             case 0: icur=icur+1; break;
  198.             case 1: icur=icur+ncols; break;
  199.             case 2: icur=icur-1; break;
  200.             case 3: icur=icur-ncols; break;
  201.             }
  202.             Grid[icur].bdir=(choice+2)%4;
  203.             Grid[icur].eastwall=Grid[icur].northwall=
  204.                 Grid[icur].westwall=Grid[icur].southwall=1;
  205.         } else {
  206.             switch (Grid[icur].bdir) {
  207.             case 0: Grid[icur].eastwall=0; icur=icur+1;
  208.                 Grid[icur].westwall=0; break;
  209.             case 1: Grid[icur].northwall=0; icur=icur+ncols;
  210.                 Grid[icur].southwall=0; break;
  211.             case 2: Grid[icur].westwall=0; icur=icur-1;
  212.                 Grid[icur].eastwall=0; break;
  213.             case 3: Grid[icur].southwall=0; icur=icur-ncols;
  214.                 Grid[icur].northwall=0; break;
  215.             case 4: goto finish;
  216.             }
  217.         }
  218.     }
  219.  finish:
  220.     [picker1 free];
  221.     [picker2 free];
  222.  
  223.     /* walls are set; clear the "last" fields again */
  224.     [self clearMaze];
  225.  
  226.     return self;
  227. }
  228.  
  229. - firstStep
  230. {
  231.     /* choose a random starting place and direction */
  232.     icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
  233.     dcur=random()%4;
  234.     Grid[icur].bdir=4;
  235.     ifirst=icur;
  236.     Grid[icur].next=(-1);
  237.  
  238.     /* set the current colors */
  239.     if (randomColor) {
  240.         curWallColor=RandomColor();
  241.         curPathColor=RandomColor();
  242.     } else {
  243.         curWallColor=wallColor;
  244.         curPathColor=pathColor;
  245.     }
  246.  
  247.     if (sharedInspectorPanel) {
  248.         if (panelMazeView) {
  249.             [panelMazeView setWallColor:curWallColor pathColor:curPathColor];
  250.         }
  251.         [sharedInspectorPanel display];
  252.     }
  253.  
  254.     /* clear drawing buffers */
  255.     wallRectListSize=0;
  256.     pathRectListSize=0;
  257.  
  258.     /* clear drawing etc */
  259.     PSsetgray(NX_BLACK);
  260.     NXRectFill(&bounds);
  261.     [self drawForward:icur];
  262.     [self flushDrawing];
  263.  
  264.     /* start countDown */
  265.     countDown=(nrows-2)*(ncols-2)*2-2;
  266.  
  267.     return self;
  268. }
  269.  
  270. /* here's where the maze solving and drawing is done */
  271. - oneStep
  272. {
  273.     int inew;
  274.     BStimeval curtime;
  275.  
  276.     /* enforce the delay between frames */
  277.     curtime=currentTimeInMs();
  278.     if (curtime-lasttime<delay) {
  279.         if (delay-(curtime-lasttime)>30) {
  280.             usleep(15000);
  281.             return self;
  282.         }
  283.         usleep((delay-(curtime-lasttime))*1000);
  284.         curtime=currentTimeInMs();
  285.     }
  286.     lasttime=curtime;
  287.  
  288.     /* check if finished */
  289.     if (countDown<=0) {
  290.         /* do the following only when recycling, not when running 1st time */
  291.         if (!countDown) {
  292.             [self drawBack:icur];
  293.             [self flushDrawing];
  294.             NXPing();
  295.             if (panelMazeView) {
  296.                 [panelMazeView initMaze];
  297.             }
  298.             [self initMaze];
  299.         }
  300.         [self firstStep];
  301.         return self;
  302.     }
  303.     countDown--;
  304.  
  305.     /* follow-the-right-wall algorithm... switch fallthrough intentional */
  306.     inew=icur;
  307.  
  308.     switch (dcur) {
  309.     case 0:
  310.         if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
  311.     case 1:
  312.         if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
  313.     case 2:
  314.         if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
  315.     case 3:
  316.         if (!Grid[icur].southwall) {dcur=3; inew=icur-ncols; break;}
  317.         if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
  318.         if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
  319.         if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
  320.     }
  321.  
  322.     if (Grid[icur].bdir==dcur) {
  323.         [self drawBack:icur];
  324.         Grid[icur].bdir=(-1);
  325.     } else {
  326.         Grid[inew].next=ifirst;
  327.         ifirst=inew;
  328.         Grid[inew].bdir=(dcur+2)%4;
  329.         [self drawForward:inew];
  330.     }
  331.  
  332.     [self flushDrawing];
  333.  
  334.     icur=inew;
  335.     dcur=(dcur+3)%4;
  336.     return self;
  337. }
  338.  
  339.  
  340. - drawForward:(int) index
  341. {
  342.     int x,y;
  343.  
  344.     x=(index%ncols)*cellSize+xoffset;
  345.     y=(index/ncols)*cellSize+yoffset;
  346.     switch (Grid[index].bdir) {
  347.     case 0:
  348.         [self addPathRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
  349.                     size: cellSize:pathSize]; break;
  350.     case 1:
  351.         [self addPathRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
  352.                     size: pathSize:cellSize]; break;
  353.     case 2:
  354.         [self addPathRectOrigin: x-gapSize:y+wallSize+gapSize
  355.                     size: cellSize:pathSize]; break;
  356.     case 3:
  357.         [self addPathRectOrigin: x+wallSize+gapSize:y-gapSize
  358.                     size: pathSize:cellSize]; break;
  359.     case 4:
  360.         [self addPathRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
  361.               size: pathSize:pathSize]; break;
  362.     }
  363.  
  364.     if (Grid[index].eastwall && Grid[index+1].next==(-2)) {
  365.         [self addWallRectOrigin:x+cellSize:y size:wallSize:cellSize+wallSize];
  366.     }
  367.     if (Grid[index].northwall && Grid[index+ncols].next==(-2)) {
  368.         [self addWallRectOrigin:x:y+cellSize size:cellSize+wallSize:wallSize];
  369.     }
  370.     if (Grid[index].westwall) {
  371.         [self addWallRectOrigin:x:y size:wallSize:cellSize+wallSize];
  372.     }
  373.     if (Grid[index].southwall) {
  374.         [self addWallRectOrigin:x:y size:cellSize+wallSize:wallSize];
  375.     }
  376.  
  377.     return self;
  378. }
  379.  
  380. - drawBack:(int) index
  381. {
  382.     int x,y;
  383.  
  384.     x=(index%ncols)*cellSize+xoffset;
  385.     y=(index/ncols)*cellSize+yoffset;
  386.  
  387.     [self addEraseRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
  388.           size:pathSize:pathSize];
  389.  
  390.     switch (Grid[index].bdir) {
  391.     case 0:
  392.         [self addEraseRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
  393.                     size: cellSize:pathSize]; break;
  394.     case 1:
  395.         [self addEraseRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
  396.                     size: pathSize:cellSize]; break;
  397.     case 2:
  398.         [self addEraseRectOrigin: x-gapSize:y+wallSize+gapSize
  399.                     size: cellSize:pathSize]; break;
  400.     case 3:
  401.         [self addEraseRectOrigin: x+wallSize+gapSize:y-gapSize
  402.                     size: pathSize:cellSize]; break;
  403.     case 4:
  404.         [self addEraseRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
  405.               size: pathSize:pathSize]; break;
  406.     }
  407.  
  408.     return self;
  409. }
  410.  
  411. - addWallRectOrigin:(int) x :(int) y size:(int)w :(int) h
  412. {
  413.     wallRectList[wallRectListSize].origin.x=x;
  414.     wallRectList[wallRectListSize].origin.y=y;
  415.     wallRectList[wallRectListSize].size.height=h;
  416.     wallRectList[wallRectListSize].size.width=w;
  417.     if (++wallRectListSize>=RECTBUFSIZE) {
  418.         [self flushWallRects];
  419.     }
  420.     return self;
  421. }
  422.  
  423. - flushWallRects
  424. {
  425.     if (wallRectListSize) {
  426.         NXSetColor(curWallColor);
  427.         NXRectFillList(wallRectList, wallRectListSize);
  428.         wallRectListSize=0;
  429.     }
  430.     return self;
  431. }
  432.     
  433. - addPathRectOrigin:(int) x :(int) y size:(int)w :(int) h
  434. {
  435.     pathRectList[pathRectListSize].origin.x=x;
  436.     pathRectList[pathRectListSize].origin.y=y;
  437.     pathRectList[pathRectListSize].size.height=h;
  438.     pathRectList[pathRectListSize].size.width=w;
  439.     if (++pathRectListSize>=RECTBUFSIZE) {
  440.         [self flushPathRects];
  441.     }
  442.     return self;
  443. }
  444.  
  445. - flushPathRects
  446. {
  447.     if (pathRectListSize) {
  448.         NXSetColor(curPathColor);
  449.         NXRectFillList(pathRectList, pathRectListSize);
  450.         pathRectListSize=0;
  451.     }
  452.     return self;
  453. }
  454.  
  455. - addEraseRectOrigin:(int) x :(int) y size:(int)w :(int) h
  456. {
  457.     eraseRectList[eraseRectListSize].origin.x=x;
  458.     eraseRectList[eraseRectListSize].origin.y=y;
  459.     eraseRectList[eraseRectListSize].size.height=h;
  460.     eraseRectList[eraseRectListSize].size.width=w;
  461.     if (++eraseRectListSize>=RECTBUFSIZE) {
  462.         [self flushEraseRects];
  463.     }
  464.     return self;
  465. }
  466.  
  467. - flushEraseRects
  468. {
  469.     if (eraseRectListSize) {
  470.         PSsetgray(NX_BLACK);
  471.         NXRectFillList(eraseRectList, eraseRectListSize);
  472.         eraseRectListSize=0;
  473.     }
  474.     return self;
  475. }
  476.  
  477. - flushDrawing
  478. {
  479.     [self flushWallRects];
  480.     [self flushPathRects];
  481.     [self flushEraseRects];
  482.     return self;
  483. }
  484.         
  485. - drawSelf:(const NXRect *)rects :(int)rectCount
  486. {
  487.     int i;
  488.     for (i=ifirst; i>=0; i=Grid[i].next) {
  489.         int x,y;
  490.         x=i%ncols; y=i/ncols;
  491.         if (x*cellSize-gapSize+xoffset<rects[0].origin.x+rects[0].size.width &&
  492.            x*cellSize+cellSize+wallSize+gapSize+xoffset>rects[0].origin.x &&
  493.            y*cellSize-gapSize+yoffset<rects[0].origin.y+rects[0].size.height &&
  494.            y*cellSize+cellSize+wallSize+gapSize+yoffset>rects[0].origin.y) {
  495.             [self drawForward:i];
  496.         }
  497.     }
  498.     [self flushDrawing];
  499.     return self;
  500. }
  501.  
  502. - (const char *) windowTitle
  503. {    return "Maze";
  504. }
  505.  
  506. - initFrame:(const NXRect *)frameRect
  507. {
  508.     Grid=NULL;
  509.     [self getMazeDefaults];
  510.     [super initFrame:frameRect];
  511.     [self initMaze];
  512.     return self;
  513. }
  514.  
  515. - free
  516. {
  517.     if (Grid) NXZoneFree([self zone], Grid);
  518.     Grid=NULL;
  519.     return [super free];
  520. }
  521.  
  522. static void ColorToString(NXColor color, char *str)
  523. {
  524.     sprintf(str,"%f %f %f",NXRedComponent(color),
  525.             NXGreenComponent(color),NXBlueComponent(color));
  526. }
  527.  
  528. static NXColor ColorFromString(const char *str)
  529. {
  530.     NXColor color;
  531.     float r,g,b;
  532.     sscanf(str,"%f %f %f",&r,&g,&b);
  533.     r=(r<0.0 ? 0.0 : (r>1.0 ? 1.0 : r));
  534.     g=(g<0.0 ? 0.0 : (g>1.0 ? 1.0 : g));
  535.     b=(b<0.0 ? 0.0 : (b>1.0 ? 1.0 : b));
  536.     color=NXConvertRGBToColor(r,g,b);
  537.     return color;
  538. }
  539.  
  540. static NXColor RandomColor()
  541. {
  542.     NXColor color;
  543.     float r,g,b;
  544.     r=((random()%65536)/65536.0)/2;
  545.     g=((random()%65536)/65536.0)/2;
  546.     b=((random()%65536)/65536.0)/2;
  547.     color=NXConvertRGBToColor(r,g,b);
  548.     return color;
  549. }
  550.  
  551. - getMazeDefaults
  552. {
  553.     char c;
  554.  
  555.     static NXDefaultsVector MazeDefaults = {
  556.         {"MazeFrameDelay",  "16"},
  557.         {"MazeRandomColor", "No"},
  558.         {"MazeWallColor",   "0.333338 0.066668 0.066668"},
  559.         {"MazePathColor",   "0.200003 0.333338 0.466674"},
  560.         {"MazeWallSize",   "2"},
  561.         {"MazePathSize",   "4"},
  562.         {"MazeGapSize",    "1"},
  563.         {NULL}
  564.     };
  565.     
  566.     NXRegisterDefaults([NXApp appName],MazeDefaults);
  567.     sscanf(NXGetDefaultValue([NXApp appName],"MazeFrameDelay"),"%d",&delay);
  568.     if (delay<0) delay=0;
  569.     if (delay>MAXDELAY) delay=MAXDELAY;
  570.     if ((c=*NXGetDefaultValue([NXApp appName],"MazeRandomColor"))=='y' ||
  571.          c=='Y') {
  572.         randomColor=YES;
  573.     } else {
  574.         randomColor=NO;
  575.     }
  576.     wallColor=
  577.       ColorFromString(NXGetDefaultValue([NXApp appName],"MazeWallColor"));
  578.     pathColor=
  579.       ColorFromString(NXGetDefaultValue([NXApp appName],"MazePathColor"));
  580.     sscanf(NXGetDefaultValue([NXApp appName],"MazeWallSize"),"%d",&wallSize);
  581.     sscanf(NXGetDefaultValue([NXApp appName],"MazePathSize"),"%d",&pathSize);
  582.     sscanf(NXGetDefaultValue([NXApp appName],"MazeGapSize"),"%d",&gapSize);
  583.     cellSize=pathSize+wallSize+2*gapSize;
  584.     if (cellSize<MINCELLSIZE) {
  585.         pathSize+=MINCELLSIZE-cellSize;
  586.         cellSize=MINCELLSIZE;
  587.     }
  588.  
  589.     return self;
  590. }
  591.  
  592.  
  593. - sizeTo:(NXCoord)width :(NXCoord)height
  594. {
  595.     [super sizeTo:width :height];
  596.     [self initMaze];
  597.     return self;
  598. }
  599.  
  600. - clearMaze
  601. {
  602.     int x,y;
  603.  
  604.     icur = -1;
  605.  
  606.     /* clear the field */
  607.     for (y=0; y<nrows; y++) {
  608.         for (x=0; x<ncols; x++) {
  609.             if (x==0 || y==0 || x==ncols-1 || y==nrows-1) {
  610.                 Grid[x+y*ncols].bdir=4;
  611.             } else {
  612.                 Grid[x+y*ncols].bdir=(-1);
  613.             }
  614.             Grid[x+y*ncols].next=(-2);
  615.     }
  616.     }
  617.  
  618.     return self;
  619. }
  620.  
  621.  
  622. /* quick update called when color has been changed */
  623. - updateViews: sender
  624. {
  625.     /* update the panel view, if needed */
  626.     if (sharedInspectorPanel) {
  627.         if (panelMazeView) {
  628.             [panelMazeView setWallColor:curWallColor pathColor:curPathColor];
  629.         }
  630.         [sharedInspectorPanel display];
  631.     }
  632.  
  633.     /* update myself */
  634.     if ([self canDraw]) {
  635.         [self lockFocus];
  636.         [self display];
  637.         [self unlockFocus];
  638.     }
  639.  
  640.     return self;
  641. }
  642.  
  643. - inspector:sender
  644. {
  645.     char buf[MAXPATHLEN];
  646.     if (!sharedInspectorPanel) {
  647.         sprintf(buf,"%s/MazeInspector.nib",[sender moduleDirectory:"Maze"]);
  648.         [NXApp loadNibFile:buf owner:self withNames:NO];
  649.         /* initialize some of the panel objects... */
  650.         if (panelWallColorWell) {
  651.             [panelWallColorWell setColor:wallColor];
  652.             [panelWallColorWell setEnabled:!randomColor];
  653.         }
  654.         if (panelPathColorWell) {
  655.             [panelPathColorWell setColor:pathColor];
  656.             [panelPathColorWell setEnabled:!randomColor];
  657.         }
  658.         if (randomColorSwitch) [randomColorSwitch setState:randomColor];
  659.         if (panelSpeedSlider) {
  660.             if (delay<1) {
  661.                 [panelSpeedSlider setFloatValue:1.0];
  662.             } else {
  663.                 [panelSpeedSlider setFloatValue:
  664.                  (float)(-log(delay/99.0)/log(100.0))];
  665.             }
  666.         }
  667.     }
  668.     return sharedInspectorPanel;
  669. }
  670.  
  671. - inspectorInstalled
  672. {
  673.     [self hideCredits:self];
  674.     if (sharedInspectorPanel) {
  675.         [sharedInspectorPanel display];
  676.     }
  677.     return self;
  678. }
  679.  
  680. - doRandomColorSwitch:sender
  681. {
  682.     if ([sender state]) {
  683.         [panelWallColorWell setEnabled:NO];
  684.         [panelPathColorWell setEnabled:NO];
  685.         randomColor=YES;
  686.         curWallColor=RandomColor();
  687.         curPathColor=RandomColor();
  688.         NXWriteDefault([NXApp appName],"MazeRandomColor","Yes");
  689.     } else {
  690.         [panelWallColorWell setEnabled:YES];
  691.         [panelPathColorWell setEnabled:YES];
  692.         randomColor=NO;
  693.         curWallColor=wallColor;
  694.         curPathColor=pathColor;
  695.         NXWriteDefault([NXApp appName],"MazeRandomColor","No");
  696.     }
  697.     [self perform:@selector(updateViews:) with:sender
  698.           afterDelay:0 cancelPrevious:YES];
  699.     return self;
  700. }
  701.  
  702. - doSpeedSlider:sender
  703. {
  704.     char str[80];
  705.     delay=99.0*exp(-[sender floatValue]*log(100.0));
  706.     sprintf(str,"%d",delay);
  707.     NXWriteDefault([NXApp appName],"MazeFrameDelay",str);
  708.     return self;
  709. }
  710.  
  711. - takeWallColorFrom:sender
  712. {
  713.     char str[80];
  714.     curWallColor=wallColor=[(NXColorWell *)sender color];
  715.     [self perform:@selector(updateViews:) with:sender
  716.           afterDelay:0 cancelPrevious:YES];
  717.     ColorToString(wallColor,str);
  718.     NXWriteDefault([NXApp appName],"MazeWallColor",str);
  719.     return self;
  720. }
  721.  
  722. - takePathColorFrom:sender
  723. {
  724.     char str[80];
  725.     curPathColor=pathColor=[(NXColorWell *)sender color];
  726.     [self perform:@selector(updateViews:) with:sender
  727.           afterDelay:0 cancelPrevious:YES];
  728.     ColorToString(pathColor,str);
  729.     NXWriteDefault([NXApp appName],"MazePathColor",str);
  730.     return self;
  731. }
  732.  
  733. - showCredits:sender
  734. {
  735.     if (panelCreditsView && sharedInspectorPanel) {
  736.         if ([panelCreditsView isDescendantOf:sharedInspectorPanel]) {
  737.             [panelCreditsView removeFromSuperview];
  738.         }
  739.         [sharedInspectorPanel addSubview:panelCreditsView];
  740.         [sharedInspectorPanel display];
  741.     }
  742.     return self;
  743. }
  744.  
  745. - hideCredits:sender
  746. {
  747.     if (panelCreditsView && sharedInspectorPanel) {
  748.         if ([panelCreditsView isDescendantOf:sharedInspectorPanel]) {
  749.             [panelCreditsView removeFromSuperview];
  750.         }
  751.         [sharedInspectorPanel display];
  752.     }
  753.     return self;
  754. }
  755.  
  756. /* these methods are needed because we should not rearrange the view */
  757. /* hierarchy while a button is active; we queue the rearranging to be */
  758. /* done later, when nothing is locked on the view */
  759.  
  760. - doShowCredits:sender
  761. {
  762.     [self perform:@selector(showCredits:)
  763.           with:self afterDelay:0 cancelPrevious:YES];
  764.     return self;
  765. }
  766.  
  767. - doHideCredits:sender
  768. {
  769.     [self perform:@selector(hideCredits:)
  770.           with:self afterDelay:0 cancelPrevious:YES];
  771.     return self;
  772. }
  773.  
  774. @end
  775.  
  776.  
  777.